local ListView = import(".ListView")
local ListViewCellModelMetatable = import(".ListViewCellModelMetatable")

local ListViewHorizontal = class("ListViewHorizontal", ListView)

local SCROLL_SPEED = 20

function ListViewHorizontal:ctor(scrollview, params)
    -- body
    ListViewHorizontal.super.ctor(self, scrollview, params)
    local scrollType = self._scrollView:getDirection()
    assert(ccui.ScrollViewDir.horizontal == scrollType, 'ListViewHorizontal only support Horizontal')
    self._curOffsetX = 0
end

--- public
-- extendOffset 额外的 输入正值
function ListViewHorizontal:getOffsetByIndex(index, extendOffset)
    local cellModel = self._cellModels[index]
    if not cellModel then
        -- release_print("getOffsetByIndex index out of boundary")
        return 0
    end
    local offsetX = -(cellModel.pos.x + self._paddingLeft)

    if extendOffset then
        offsetX = offsetX - extendOffset
    end

    if offsetX > 0 then
        offsetX = 0
    end
    if offsetX < self._listViewWidth - self._totalWidth then
        offsetX = self._listViewWidth - self._totalWidth
    end
    return offsetX
end


function ListViewHorizontal:setListViewOffsetX(offsetX)
    self:_reCreate(offsetX)
    self._innerContainer:setPositionX(offsetX)
end

function ListViewHorizontal:jumpToIndex(index, extendOffset)
    local offsetX = self:getOffsetByIndex(index, extendOffset)
    self:setListViewOffsetX(offsetX)
end

function ListViewHorizontal:stopScrollAction(isEnd)
    if self._curScrollAction then
        self._scrollView:stopAction(self._curScrollAction)
        self._curScrollAction = nil
        if self._scrollEndCallBack then
            self._scrollEndCallBack(isEnd)
        end
        self._scrollEndCallBack = nil
    end
end

function ListViewHorizontal:scrollToIndex(index, endCallback, extendOffset, speed)
    local offsetX = self:getOffsetByIndex(index, extendOffset)
    self:scrollToOffset(offsetX, endCallback, speed)
end


function ListViewHorizontal:scrollToVisibleArea(index, endCallback, speed)
    if not self:isInVisibleIndexs(index) then
        if self._headIndex >= index then
            self:scrollToIndex(index, endCallback, 0,speed)
        else
            local cellModel = self._cellModels[index]
            if cellModel then
                self:scrollToIndex(index, endCallback, self._listViewWidth - cellModel.size.width,speed)
            end
        end
    end
end

function ListViewHorizontal:scrollToOffset(targetOffsetX, endCallback, speed)
    self:stopScrollAction()
    if not speed then
        speed = SCROLL_SPEED
    end
    self._scrollEndCallBack = endCallback
    self._curScrollAction = display.createUpdateAction(function()
        local curOffsetX = self._innerContainer:getPositionX()
        local dx = targetOffsetX - curOffsetX
        local isFinish = false
        if dx < 0 then
            if dx >= -speed then
                isFinish = true  
            else
                dx = -speed
            end
            curOffsetX = curOffsetX + dx
        else
            if dx <= speed then
                isFinish = true  
            else
                dx = speed
            end
            curOffsetX = curOffsetX + dx
        end
        self._innerContainer:setPositionX(curOffsetX)
        self:_onScroll(curOffsetX)
        if isFinish then
            self:stopScrollAction(true)
        end
    end, 0.02)
    self._scrollView:runAction(self._curScrollAction)
end

-- projected:
function ListViewHorizontal:_reloadListView(params)
    params = params or {}
    local offsetX 
    if params.startIndex then
        local startIndex = params.startIndex or 1
        local startIndexOffset = params.startIndexOffset or 0
        offsetX = self:getOffsetByIndex(startIndex,startIndexOffset)
    elseif params.offsetX then
        offsetX = params.offsetX
        if offsetX > 0 then
            offsetX = 0
        end
        if offsetX < self._listViewWidth - self._totalWidth then
            offsetX = self._listViewWidth - self._totalWidth
        end
    else
        offsetX = self._innerContainer:getPositionX()
        if offsetX > 0 then
            offsetX = 0
        end
        if offsetX < self._listViewWidth - self._totalWidth then
            offsetX = self._listViewWidth - self._totalWidth
        end
    end
    self:setListViewOffsetX(offsetX)
end

function ListViewHorizontal:_init(params)
    local eventEnums = ccui.ScrollviewEventType
    self._eventListens = {
        [eventEnums.scrollToLeft] = true,
        [eventEnums.scrollToRight] = true,
        [eventEnums.scrolling] = true,
        [eventEnums.bounceLeft] = true,
        [eventEnums.bounceRight] = true,
        [eventEnums.bounceStart] = true,
        [eventEnums.bounceStop] = true,
    }
end

function ListViewHorizontal:_initCellModels(data, params)
    self._cellModels = {}
    local gapX = self._gapX
    local totalNum = #data
    local listViewWidth = self._listViewWidth
    self._realTotalNum = totalNum
    -- 单个cell 
    if self._isSingleTag then
        if params.minDataLen and  totalNum < params.minDataLen then
            totalNum = params.minDataLen
        end
    end

    local totalWidth = 0
    for i = 1, totalNum do
        local renderData = data[i]
        local tag = self:_getTagByData(renderData)
        local config = self._cellConfigs[tag]
        assert(config ~= nil, "can not find cell config  ".. tag or "nil")
        local size = config.size    
        local model = {
            pos = cc.p(totalWidth, size.height), 
            tag = tag,
            size = size,
            index = i,
            cell = nil, 
            uiState = nil, 
            listview = self,        
        }
        setmetatable(model, {__index = ListViewCellModelMetatable})
        self._cellModels[i] = model 
        totalWidth = totalWidth + size.width + gapX
    end
    self._totalNum = totalNum

    local addMoreGapX = gapX
    if totalNum  == 1 then -- 布局的时候 多加了一个 gapX
        addMoreGapX =  0
    end
  
    totalWidth = totalWidth - addMoreGapX + self._paddingLeft + self._paddingRight
    self._totalWidth = totalWidth
    if self._totalWidth < self._listViewWidth then
        self._totalWidth = self._listViewWidth
    end
    local cellsWidth = self._totalWidth - self._paddingLeft - self._paddingRight
    self._cellsWidth = cellsWidth
    self._parentNode:setPositionX(0)
    self:_updateInnerContainerSize()
end

function ListViewHorizontal:_updateInnerContainerSize()
    self._ignoreScrollEvent = true
    self._scrollView:setInnerContainerSize(cc.size(self._totalWidth, self._listViewheight))
    self._ignoreScrollEvent = false
end


function ListViewHorizontal:getLeftOffsetXByIndex(index)
    if not index then
        return 0
    end
    local cellModel = self._cellModels[index]
    if cellModel then
        local offsetX = self._innerContainer:getPositionX()
        local left = -offsetX - self._paddingLeft
        return cellModel.pos.x - left
    end
    return 0
end

function ListViewHorizontal:_reCreate(offsetX)
    self:_clean()
    if not offsetX then
        offsetX = self._innerContainer:getPositionX()
        if offsetX > 0 then
            offsetX = 0
        end
        if offsetX < self._listViewWidth - self._totalWidth then
            offsetX = self._listViewWidth - self._totalWidth
        end
    end
    local left = -offsetX - self._paddingLeft
    local right = -offsetX + self._listViewWidth
    local gapX = self._gapX
    self._headIndex = 1
    self._tailIndex = 0
    
    local totalNum = self._totalNum
    local headIndex 

    for i = 1, totalNum do
        local cellModel = self._cellModels[i]
        if cellModel ~= nil and cellModel.pos.x + cellModel.size.width > left then
            headIndex = i
            break
        end
    end
    if headIndex then
        local tailIndex
        for i = headIndex, totalNum do
            local cellModel = self._cellModels[i]
            if cellModel ~= nil and cellModel.pos.x > right then
                tailIndex = i -1
                break
            end
            
            self:_newCellByModel(cellModel)
        end
        if not tailIndex then
            tailIndex = totalNum
        end
        self._headIndex = headIndex
        self._tailIndex = tailIndex
    end
end

function ListViewHorizontal:_onScroll(offsetX)
    if self._tailIndex == 0 then
        return
    end
    
    local totalNum = self._totalNum
    if offsetX > 0 then
        offsetX = 0
    end
    if offsetX < self._listViewWidth - self._totalWidth then
        offsetX = self._listViewWidth - self._totalWidth
    end

    
    local gapX = self._gapX
    local left = -offsetX - self._paddingLeft
    local right = -offsetX + self._listViewWidth
    

    local startIndex = self._headIndex
    local endIndex = self._tailIndex
    local headCellModel = self._cellModels[startIndex]
    local endCellModel = self._cellModels[endIndex]
    if headCellModel and endCellModel and 
      ((headCellModel.pos.x +  headCellModel.size.width + gapX) > left or endCellModel.pos.x < right) then
        if endCellModel.pos.x + endCellModel.size.width + gapX < right then
            -- 滑动方向 <<<<
            local isBreak = false
            for i = startIndex, endIndex, 1 do
                local cellModel = self._cellModels[i]
                if cellModel ~= nil and (cellModel.pos.x + cellModel.size.width + gapX) < left then
                    self:_recycleCellByModel(cellModel)
                else
                    self._headIndex = i
                    isBreak = true
                    break
                end
            end
            if not isBreak then
                self._headIndex = endIndex
            end
            isBreak = false
            for i = endIndex + 1, totalNum, 1 do
                local cellModel = self._cellModels[i]
                if cellModel ~= nil and cellModel.pos.x < right then
                    self:_newCellByModel(cellModel)
                else
                    self._tailIndex = i - 1
                    isBreak = true
                    break
                end
            end
            if not isBreak then
                self._tailIndex = totalNum
            end
        elseif headCellModel.pos.x > left then
             -- 滑动方向 >>>>
            local isBreak = false
            for i = endIndex, startIndex, -1 do
                local cellModel = self._cellModels[i]
                if cellModel ~= nil and cellModel.pos.x > right then
                    self:_recycleCellByModel(cellModel)
                else
                    self._tailIndex = i
                    isBreak = true
                    break
                end
            end
            if not isBreak then
                self._tailIndex = startIndex
            end
            --向上拖动
            isBreak = false
            for i = startIndex-1, 1, -1 do
                local cellModel = self._cellModels[i]
                if cellModel ~= nil and (cellModel.pos.x + cellModel.size.width + gapX) > left then
                    self:_newCellByModel(cellModel)
                else
                    self._headIndex = i + 1
                    isBreak = true
                    break
                end
            end
            if not isBreak then
                self._headIndex = 1
            end
        end
    else
        self:_reCreate()
    end
end

function ListViewHorizontal:_onEvent(scrollView, event)
    if self._allEventListen then
        self._allEventListen(scrollView, event)
    end

    if self._eventListens[event]  and self._ignoreScrollEvent == false then
        self:stopScrollAction()
        local offsetX = self._innerContainer:getPositionX()
        self:_onScroll(offsetX)
        
        if self._scrollEventListen then
            self._scrollEventListen(scrollView, event)
        end
    end
end


return ListViewHorizontal
